Skip to content

fix(llm): accept reasoning field in OpenAI-compatible streams#35284

Open
mickaeldamatha wants to merge 1 commit into
anomalyco:devfrom
mickaeldamatha:fix/openai-compatible-reasoning
Open

fix(llm): accept reasoning field in OpenAI-compatible streams#35284
mickaeldamatha wants to merge 1 commit into
anomalyco:devfrom
mickaeldamatha:fix/openai-compatible-reasoning

Conversation

@mickaeldamatha

@mickaeldamatha mickaeldamatha commented Jul 4, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #35283

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

The OpenAI Chat streaming delta schema (OpenAIChatDelta in packages/llm/src/protocols/openai-chat.ts) only declares reasoning_content. Each SSE chunk is decoded via Protocol.jsonEventSchema.fromJsonString, which strips any key that isn't on the struct. So when a provider streams its thinking under reasoning instead of reasoning_content, that key is dropped during decoding and step() never sees it — the reasoning is silently lost.

I hit this with Scaleway; it also affects self-hosted vLLM / SGLang deployments and OpenRouter's reasoning field. The model was clearly reasoning but nothing surfaced in the stream. I had been working around it by rewriting reasoningreasoning_content in a proxy in front of opencode serve; this makes that workaround unnecessary.

Two changes:

  • add reasoning to OpenAIChatDelta so the decoder keeps the field
  • in step(), read delta.reasoning_content ?? delta.reasoning, so reasoning_content still takes precedence when a provider sends both

It's additive to the wire schema — providers that only send reasoning_content (DeepSeek etc.) take the exact same path as before.

How did you verify your code works?

Added two streaming tests in packages/llm/test/provider/openai-chat.test.ts, next to the existing reasoning_content one:

  • reasoning arriving via reasoning emits the expected reasoning-start / reasoning-delta / reasoning-end events
  • when both fields are present, reasoning_content wins

bun test in packages/llm: 29 pass / 0 fail. bun typecheck clean (the pre-push hook also runs the full monorepo typecheck, 30/30).

Screenshots / recordings

N/A — not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Some OpenAI-compatible providers (Scaleway, vLLM/SGLang, OpenRouter, ...)
stream chain-of-thought under a `reasoning` delta field instead of the
`reasoning_content` field emitted by DeepSeek et al. The OpenAI Chat
protocol only declared and read `reasoning_content`, so `reasoning` was
stripped by the delta schema and the model's thinking was silently
dropped for those providers.

Declare `reasoning` on the delta schema and fall back to it when
`reasoning_content` is absent, preferring `reasoning_content` when both
are present. Adds streaming tests for both cases.
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels Jul 4, 2026
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenAI-compatible reasoning streamed under reasoning field is dropped

1 participant